/**
 * @class Bible.util.Format
 * Reusable data formatting functions
 * @singleton
 */
Bible.util.Format = function(){

    var BORDER  = "border",
        LEFT    = "-left",
        RIGHT   = "-right",
        TOP     = "-top",
        BOTTOM  = "-bottom",
        WIDTH   = "-width",
        STYLE   = "-style",
        COLOR   = "-color",
        BORDERS = [TOP, RIGHT, BOTTOM, LEFT],
        cssRe   = /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
        borderRe   = /([0-9-]+)px\s([a-z]+)\s([#a-f0-9]+)/gi,
        borderWidthRe   = /border-([a-z]+)-width/gi,
        colorRe   = /([a-z-]+)-color/gi,
		keywordSpanTpl = new Ext.XTemplate('<span class="keyword {cls}" style="{style}">'),
		keywordTextTpl = new Ext.XTemplate('<span class="keyword {cls}" style="{style}">{text}</span>');


    function divideBorders(styles){
        var border = styles[BORDER];
        // divide border: in 4
        if(border){
            styles[BORDER + TOP]    = border;
            styles[BORDER + RIGHT]  = border;
            styles[BORDER + BOTTOM] = border;
            styles[BORDER + LEFT]   = border;
            delete styles[BORDER];
        }

        // divide border-XXX in 3
        Ext.each(BORDERS, function(position){
            border = styles[BORDER + position];
            if(border){
                borderRe.lastIndex = 0;
                var matches = borderRe.exec(border);
                //console.debug(matches[1],' : ',matches[2],' : ',matches[3]);
                styles[BORDER + position + WIDTH] = matches[1];
                styles[BORDER + position + STYLE] = matches[2];
                styles[BORDER + position + COLOR] = matches[3];
                delete styles[BORDER + position];
            }
        });
    }

    function combineBorders(styles){
        var borderWidth,
            allBorders;

        // combine in border-XXX
        Ext.each(BORDERS, function(position){
            borderWidth = styles[BORDER + position + WIDTH];
            if(borderWidth && borderWidth != '0px'){
                styles[BORDER + position] = String.format("{0} {1} {2}", borderWidth,
                        styles[BORDER + position + STYLE],
                        styles[BORDER + position + COLOR] || 'FFFFFF');
            }
            delete styles[BORDER + position + WIDTH];
            delete styles[BORDER + position + STYLE];
            delete styles[BORDER + position + COLOR];
        });

        // combine 4 border in 1
        allBorders = styles[BORDER + TOP];
        if(allBorders &&
                allBorders == styles[BORDER + RIGHT] &&
                allBorders == styles[BORDER + BOTTOM] &&
                allBorders == styles[BORDER + LEFT]){
            styles[BORDER] = allBorders;
            delete styles[BORDER + TOP];
            delete styles[BORDER + RIGHT];
            delete styles[BORDER + BOTTOM];
            delete styles[BORDER + LEFT];
        }
        return styles;
    }

    return {

        /**
         *
         * @param {String} style
         * @param {Boolean} minimized optional
         */
        getStyleAsObject: function(style, minimized){
            var matches,
                styles = {};

            //console.debug('getStyleAsObject', style, minimized);

            // divide style String in styles Object
            cssRe.lastIndex = 0;
            while ((matches = cssRe.exec(style))) {
                //console.debug(matches[1], ' : ', matches[1], '  :   ',matches[2]);
                styles[matches[1]] = matches[2];
            }

            //console.debug('===== convert style from String to Object =====');
            //console.dir(styles);
            if(!minimized){
                //console.debug('===== divide border-XXX in 3 =====');
                divideBorders(styles);
                //console.dir(styles);
            }

            return styles;
        },

        /**
         *
         * @param {Object/JSON} styles
         * @param {Boolean} minimized optional
         */
        getStyleAsString: function(styles, minimized){
            var style = '';

            //console.debug('getStyleAsString', styles, minimized);
            this.normalizeStyles(styles);

            //console.dir(styles);
            if(minimized){
                combineBorders(styles);
                //console.debug('===== divide border-XXX in 3 =====');
                //console.dir(styles);
            }

            // convert object to string
            Ext.iterate(styles, function(key, value) {
                if(value != ''){
                    style += String.format("{0}: {1}; ", key, value);
                }
            }, this);

            return style;
        },

        /**
         * add px at the end of -width elements
         * add # at the beginning of -width elements
         * @param {Object} styles
         */
        normalizeStyles: function(styles){
            Ext.iterate(styles, function(key, value) {
                if(value != ''){
                    if(key.match(borderWidthRe)){
                        styles[key] = value + 'px';
                    } else if(key.match(colorRe)){
                        styles[key] = '#' + value;
                    }
                } else {
                    delete styles[key];
                }
            }, this);
            return styles;
        },

        /**
         * @param {String} style
         * @param {String} cls
         * @return {String} span begin tag with class="keyword {cls}", style="{style}". no text inside
         */
        keywordSpan: function(style, cls){
            return (keywordSpanTpl.apply({
                cls: cls,
                style: style
            }));
        },

        /**
         * @param {String} text
         * @param {String} style
         * @param {String} cls
         * @return {String} span with class="keyword {cls}", style="{style}" and text inside
         */
        keywordText: function(text, style, cls){
            return (keywordTextTpl.apply({
                cls: cls,
                style: style,
                text: text
            }));
            //return String.format('<span class="keyword {0}" style="{1}">{2}</span>', cls, style, text);
        },

        /**
         * Return a string with ... at the end, such as the size is less than maxWidth,
         * when applying the styles of container container.
         * @param {String} str
         * @param {Number} maxWidth
         * @param {Ext.Container} container
         * @return {String}
         */
        ellipsis: function(str, maxWidth, container){
            var c = container ? container : Ext.getBody(),
                elWidth = Ext.util.TextMetrics.measure(c, str).width;
            while(elWidth > maxWidth){
                str = Ext.util.Format.ellipsis(str, str.length-1);
                elWidth = Ext.util.TextMetrics.measure(c, str).width;
            }
            return str;
        }
    }
}();